//=============================================================================
// IMPLEMENTACIO DE LA CLASSE 'FILEMANAGER'
//=============================================================================
// per Marc Delgado (marcdelgado@gmail.com) | rev. 2012.06.11

#include "fileManager.hpp"


// FUNCIONS PUBLIQUES
///////////////////////////////////////////////////////////////////////////////


//CONSTRUCTORA que rep la ruta del disc virtual.
fileManager::fileManager(char* pDisk)
{
    _dman = new directoryManager(pDisk);
    _actFile = NULL;
}


//CREARF: crea un fitxer dintre del disc virtual.
bool fileManager::crearf(char* pFile, const unsigned char &pType, const unsigned char &pMR, const unsigned char &pMCC)
{
    bool ok = _dman->make(pFile, pType, pMR, pMCC);
    return ok;
}


//DELF: esborra un fitxer dintre del disc virtual.
bool fileManager::delf(char* pFile)
{
    bool ok = _dman->del(pFile);
    return ok;
}


//INSERTR: insereix un registre en un fitxer.
bool fileManager::insertr(char* pFile, char* pKey, char* pData)
{
    //Activem el fitxer demanat.
    _actFile = pFile;
    //Comprobem si existeix l'arxiu.
    llistaPags lp = _dman->list(_actFile);
    bool ok = !lp.empty();
    if(ok)
    {
        //Cerquem forat buit a les pàgines del fitxer.
        hole myHole = findEmpty();
        //Fussionem les cadenes clau-dades.
        char* registre = pKey;
        strcat(registre, pData);
        //Insertem el registre.
        writeReg(myHole, pKey);
    }
    return ok;
}


//DELR: esborra un registre en un fitxer.
bool fileManager::delr(char* pFile, char* pKey)
{
    //Activem el fitxer demanat.
    _actFile = pFile;
    //Comprobem si existeix l'arxiu.
    llistaPags lp = _dman->list(_actFile);
    bool ok = !lp.empty();
    if(ok)
    {
        //Cerquem forat buit a les pàgines del fitxer.
        hole myHole = findKey(pKey);
        //Fussionem les cadenes clau-dades.
        char* blanc = new char[getSizeReg()];
        for (int i = 0; i < getSizeReg(); i++) blanc[i] = 0;
        //Esborrem el registre.
        ok = writeReg(myHole, blanc);
    }
    return ok;
}


//QUERY: permet consultar per rang o coincidencia, en un registre.
pair<list<char*>,int> fileManager::query(char* pFile, const char &pType, char* pKey1, char* pKey2)
{
    //Activem el fitxer demanat.
    _actFile = pFile;
    //Apliquem mida de clau objecte de retorn.
    pair<list<char*>,int> ret;
    ret.second = getSizeKey();
    //Obtenim llista de pàgines.
    llistaPags lp = _dman->list(_actFile);

    char* myStr = new char[getSizeReg()];
    hole myHole; myHole.first = 0; myHole.second = 0;

    while(!lp.empty())
    {
        myHole.first = lp.back();
        for(int i = 0; i < getMaxRegs(); i++)
        {
            myHole.second = i;
            myStr = readReg(myHole);
            //Si coincideix, afegir a la llista de retorn.
            if (strncmp(myStr, pKey1, getSizeKey()) >= 0)
                if (strncmp(myStr, pKey2, getSizeKey()) <= 0)
                    ret.first.push_front(myStr);
        }
        lp.pop_back();
    }
    return ret;
}


//DESTRUCTORA que tanca el fitxer.
fileManager::~fileManager()
{
    delete(_dman);
}


// FUNCIONS AUXILIARS
///////////////////////////////////////////////////////////////////////////////


//READREG: retorna un punter a un registre, segons la coordenada de 'hole'.
char* fileManager::readReg(const hole &pHole)
{
    bool ok = false;
    char* myReg = new char[getSizeReg()];
    //Comprobar que la pàgina pertany al fitxer.
    llistaPags lp = _dman->list(_actFile);
   while(!lp.empty() and !ok){
        ok = (lp.back() == pHole.first);
        if(!ok) lp.pop_back();
    }
    //Si l'hem trobat, comprobar que la posició es un valor coherent.
    unsigned short length = getSizeReg();
    ok = ok and (length * pHole.second) < (_sPage - _sOffLeaf - length);

    //Si tot és OK, procedim a retornar el punter al registre.
    if(ok){
        myReg = _dman->readPage(pHole.first);
        myReg = &myReg[_sOffLeaf + (pHole.second*length)];
    }
    return myReg;
}


//WRITEREG: escriu un registre en la pàgina-forat indicats.
bool fileManager::writeReg(const hole &pHole, char* pReg)
{
    bool ok = false;
    //Comprobar que la pàgina pertany al fitxer.
    llistaPags lp = _dman->list(_actFile);
    while(!lp.empty() and !ok){
        ok = (lp.back() == pHole.first);
        if(!ok) lp.pop_back();
    }
    //Si l'hem trobat, comprobar que la posició es un valor coherent.
    unsigned short length = getSizeReg();
    ok = ok and (length * pHole.second) < (_sPage - _sOffLeaf - length);
    //Si tot és OK, procedim a copiar el registre a pàgina-posició.
    if(ok){

        char* registre = new char[getSizeReg()];
        registre = _dman->readPage(pHole.first);
        registre = &registre[_sOffLeaf + pHole.second*length];
        strncpy(registre, pReg, getSizeReg());
        _dman->writePage(pHole.first);
    }
    return ok;
}


//FINDKEY: cerca una clau dins d'un fitxer i retorna la pàgina-forat.
hole fileManager::findKey(char* pKey)
{
    bool ok = false;
    //Obtenir llistat de pàgines.
    llistaPags lp = _dman->list(_actFile);
    //Cercar forat lliure.
    hole myHole; myHole.first = 0; myHole.second = 0;
    char* myPag = new char[_sPage];

    while(!lp.empty() and !ok){
        myPag = _dman->readPage(lp.back());
        for (unsigned short i = 0; i < getMaxRegs() and !ok; i++)
        {
            char* myStr = &myPag[_sOffLeaf + i*getSizeReg()];
            if (strncmp(myStr,pKey,getSizeKey()) == 0)
            {
                ok = true;
                myHole.first = lp.back();
                myHole.second = i;
            }
        }
        if(!ok) lp.pop_back();
    }
    return myHole;
}


//FINDEMPTY: cerca la primera pàgina-forat buit dins d'un fitxer.
hole fileManager::findEmpty()
{
    bool ok = false;
    //Obtenir llistat de pàgines.
    llistaPags lp = _dman->list(_actFile);
    //Cercar forat lliure.
    hole myHole; myHole.first = 0; myHole.second = 0;
    char* myPag = new char[_sPage];

    while(!lp.empty() and !ok){
        myPag = _dman->readPage(lp.back());
        for (unsigned short i = 0; i < getMaxRegs() and !ok; i++)
        {
            char myStr = myPag[_sOffLeaf + i*getSizeReg()];
            if (myStr == 0)
            {
                ok = true;
                myHole.first = lp.back();
                myHole.second = i;
            }
        }
        if(!ok) lp.pop_back();
    }
    //Si no hi cap a les pàgines actuals, insertem una de nova.
    if(!ok and lp.empty())
    {
        myHole.first = _dman->addpag(_actFile);
        myHole.second = 0;
    }
    return myHole;
}


// FUNCIONS PRIVADES - GETTERS
///////////////////////////////////////////////////////////////////////////////


//GETMAXREGS: retorna el nombre màxim de registres que caben a una pàgina, segons el fitxer.
unsigned short fileManager::getMaxRegs()
{
    properties prp = _dman->getProperties(_actFile);
    return (_sPage - _sOffLeaf)/ (int)(unsigned char)prp.mr;
}


//GETSIZEREG: retorna la mida del registre, segons el fitxer.
unsigned char fileManager::getSizeReg()
{
    properties prp = _dman->getProperties(_actFile);
    return prp.mr;
}


//GETSIZEKEY: retorna la mida de la clau, segons el fitxer.
unsigned char fileManager::getSizeKey()
{
    properties prp = _dman->getProperties(_actFile);
    return prp.mcc;
}
